"""
3D geometry helpers (analytic).

Contains line-segment projections, distances, and analytic segment-sphere intersection.
"""

from __future__ import annotations
import numpy as np
from typing import Tuple

Vec3 = np.ndarray


def closest_point_on_segment(A: Vec3, B: Vec3, C: Vec3) -> Tuple[Vec3, float]:
    """
    求点C到线段AB的最近点P，以及线段参数s (P = A + s(B-A))
    """
    A = np.asarray(A, dtype=float)
    B = np.asarray(B, dtype=float)
    C = np.asarray(C, dtype=float)
    AB = B - A
    denom = float(np.dot(AB, AB))
    if denom == 0.0:
        return A, 0.0
    s = float(np.dot(C - A, AB) / denom)
    if s < 0.0:
        s = 0.0
    elif s > 1.0:
        s = 1.0
    P = A + s * AB
    return P, s


def distance_point_to_segment(A: Vec3, B: Vec3, C: Vec3) -> float:
    """求解点C到线段AB的距离
    """
    P, _ = closest_point_on_segment(A, B, C)
    return float(np.linalg.norm(P - C))


def segment_intersects_sphere(A: Vec3, B: Vec3, center: Vec3, r: float) -> bool:
    """
    判断线段AB是否与球(center,r)相交或相切
    """
    A = np.asarray(A, dtype=float)
    B = np.asarray(B, dtype=float)
    C = np.asarray(center, dtype=float)
    d = B - A
    f = A - C
    a = float(np.dot(d, d))
    b = 2.0 * float(np.dot(f, d))
    c = float(np.dot(f, f) - r * r)
    disc = b * b - 4 * a * c
    if disc < 0.0:
        return False
    sqrtD = disc ** 0.5
    u1 = (-b - sqrtD) / (2 * a)
    u2 = (-b + sqrtD) / (2 * a)
    if 0.0 <= u1 <= 1.0 or 0.0 <= u2 <= 1.0:
        return True
    # Segment entirely inside sphere (rare but possible if c<0 at u in (0,1))
    if c <= 0.0:
        return True
    return False